﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TCG.Engine.Libraries;
using TCG.Engine.Core;
using TCG.Engine.Effects;

namespace TCG.Engine.Impacts
{
    public abstract class ImpactBase: IImpact
    {
        public TargerInfo Target { get; set; }
        public ImpactTypeInfo Type { get; private set; }
        public List<string> Classes { get; private set; }

        public ImpactBase(TargerInfo target, ImpactTypeInfo type)
        {
            this.Target = target;
            this.Type = type;
            this.Classes = new List<string>();
        }

        public abstract void Apply(MoveContext mc);

        protected Player GetTarget(MoveContext mc)
        {
            return Target == TargerInfo.Self ? mc.ActivePlayer : mc.EnemyPlayer;
        }
    }

    public class SimpleImpact: ImpactBase
    {
        Action<Player, int> apply;

        public int Value { get; set; }
        public SimpleImpact(TargerInfo target, int value, Action<Player, int> apply, ImpactTypeInfo type): base(target, type) 
        {
            this.apply = apply;
            this.Value = value;
        }

        public override void Apply(MoveContext mc)
        {
            apply(GetTarget(mc), Value);
        }
    }

    public class DiscardCardImpact: ImpactBase
    {
        public DiscardCardImpact(TargerInfo target) : base(target, ImpactTypeInfo.DiscardCard)
        {
        }

        public override void Apply(MoveContext mc)
        {
            var targetPlayer = GetTarget(mc);
            var card = targetPlayer.Interaction.DiscardCard();

            targetPlayer.Hand.Remove(card);
        }
    }

    public class ApplyEffectImpact : ImpactBase
    {
        string effectId;
        public ApplyEffectImpact(string effectId)
            : base(TargerInfo.Self, ImpactTypeInfo.ApplyEffect)
        {
            this.effectId = effectId;
        }

        IEffect effect;
        public ApplyEffectImpact(IEffect effect)
            : base(TargerInfo.Self, ImpactTypeInfo.ApplyEffect)
        {
            this.effect = effect;
        }

        public override void Apply(MoveContext mc)
        {
            var lib = Env.Get<IEffectLibrary>();

            if (effect == null)
                effect = lib.Get(effectId);

            effect.Apply(mc);
        }
    }

    public static class ImpactsFactory
    {
        public static IImpact DealDamage(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.Life -= v, ImpactTypeInfo.DealDamage);
        }
        public static IImpact DealHealing(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.Life += v, ImpactTypeInfo.DealHealing);
        }
        public static IImpact AddGold(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.Gold += v, ImpactTypeInfo.AddGold);
        }
        public static IImpact AddGoldIncome(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.GoldIncome += v, ImpactTypeInfo.AddGoldIncome);
        }
        public static IImpact AddMana(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.Mana += v, ImpactTypeInfo.AddMana);
        }
        public static IImpact AddManaIncome(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.ManaIncome += v, ImpactTypeInfo.AddManaIncome);
        }
        public static IImpact DrawCard(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, 
                (p, v) => Enumerable.Range(0, value).ToList().ForEach(i => p.Draw()), 
                ImpactTypeInfo.DrawCard);
        }
        public static IImpact DiscardCard(TargerInfo target)
        {
            return new DiscardCardImpact(target);
        }
        public static IImpact CostGold(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.Gold -= v, ImpactTypeInfo.CostGold);
        }
        public static IImpact CostMana(int value, TargerInfo target)
        {
            return new SimpleImpact(target, value, (p, v) => p.Mana -= v, ImpactTypeInfo.CostMana);
        }
        public static IImpact ApplyEffect(string effectId, TargerInfo target)
        {
            return new ApplyEffectImpact(effectId);
        }

        public static IImpact ByInfo(ImpactInfo info)
        {
            Func<int> getValue = () => int.Parse(info.Value);
            switch (info.Type)
            {
                case ImpactTypeInfo.DealDamage: return DealDamage(getValue(), info.Target);
                case ImpactTypeInfo.DealHealing: return DealHealing(getValue(), info.Target);
                case ImpactTypeInfo.AddGold: return AddGold(getValue(), info.Target);
                case ImpactTypeInfo.AddGoldIncome: return AddGoldIncome(getValue(), info.Target);
                case ImpactTypeInfo.AddMana: return AddMana(getValue(), info.Target);
                case ImpactTypeInfo.AddManaIncome: return AddManaIncome(getValue(), info.Target);
                case ImpactTypeInfo.DrawCard: return DrawCard(getValue(), info.Target);
                case ImpactTypeInfo.DiscardCard: return DiscardCard(info.Target);
                case ImpactTypeInfo.CostGold: return CostGold(getValue(), info.Target);
                case ImpactTypeInfo.CostMana: return CostMana(getValue(), info.Target);
                case ImpactTypeInfo.ApplyEffect: return ApplyEffect(info.Value, info.Target);
                default:
                    throw new ApplicationException();
            }
        }
    }
}
